#include "stdafx.h"

#include "hwinexception.h"

#include <stdarg.h>

namespace harlinn
{
    namespace windows
    {

        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( SystemException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( AccessViolationException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( AggregateException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( ApplicationException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( ArgumentException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( ArgumentNullException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( ArgumentOutOfRangeException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( ArithmeticException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( BadImageFormatException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( DataMisalignedException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( TypeLoadException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( DivideByZeroException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( DllNotFoundException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( DuplicateWaitObjectException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( EntryPointNotFoundException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( MemberAccessException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( FieldAccessException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( FormatException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( IndexOutOfRangeException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( InsufficientExecutionStackException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( OutOfMemoryException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( InsufficientMemoryException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( InvalidCastException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( InvalidOperationException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( InvalidProgramException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( InvalidTimeZoneException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( MethodAccessException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( MissingMemberException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( MissingFieldException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( MissingMethodException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( MulticastNotSupportedException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( NotFiniteNumberException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( NotImplementedException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( NotSupportedException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( NullReferenceException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( OperationCanceledException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( OverflowException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( PlatformNotSupportedException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( RankException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( TimeoutException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( TypeInitializationException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( TypeUnloadedException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( UnauthorizedAccessException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( UriFormatException )
        HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( UriTemplateMatchException )

        namespace io
        {
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( IOException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( DirectoryNotFoundException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( DriveNotFoundException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( EndOfStreamException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( FileLoadException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( FileNotFoundException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( PathTooLongException )
            HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( PipeException )
        };


        // ----------------------------------------------------------------------
        // ExceptionData
        // ----------------------------------------------------------------------
        HWIN_EXPORT ExceptionData::ExceptionData()
            : hResult(0),
              code(0),
              innerException(nullptr)
        {
        }

        HWIN_EXPORT ExceptionData::ExceptionData(const ExceptionData& theException)
            : message(theException.message),
              helpLink(theException.helpLink),
              source(theException.source),
              hResult(theException.hResult),
              code(theException.code),
              innerException(nullptr)
        {
            if(theException.innerException)
            {
                innerException = theException.innerException->Clone();
            }
        }

        HWIN_EXPORT ExceptionData::ExceptionData(const char* theMessage)
            : message(theMessage),
              hResult(0),
              code(0),
              innerException(nullptr)
        {
        }
        HWIN_EXPORT ExceptionData::ExceptionData(const std::string& theMessage)
            : message(theMessage),
              hResult(0),
              code(0),
              innerException(nullptr)
        {
        }
        HWIN_EXPORT ExceptionData::ExceptionData(const char* theMessage, const ExceptionData* theInnerException)
            : message(theMessage),
              hResult(0),
              code(0),
              innerException(nullptr)
        {
            if(theInnerException)
            {
                innerException = theInnerException->Clone();
            }
        }
        HWIN_EXPORT ExceptionData::~ExceptionData( )
        {
            if(innerException)
            {
                delete innerException;
            }
        }

        HWIN_EXPORT ExceptionData* ExceptionData::Clone() const
        {
            ExceptionData* result = new ExceptionData(*this);
            return result;
        }

        HWIN_EXPORT const std::string& ExceptionData::GetMessage() const
        {
            return message;
        }
        HWIN_EXPORT std::string ExceptionData::GetMessage()
        {
            return message;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetMessage(const char* theValue)
        {
            message = theValue;
            return *this;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetMessage(const std::string& theValue)
        {
            message = theValue;
            return *this;
        }

        HWIN_EXPORT const std::string& ExceptionData::GetHelpLink() const
        {
            return helpLink;
        }
        HWIN_EXPORT std::string ExceptionData::GetHelpLink()
        {
            return helpLink;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetHelpLink(const char* theValue)
        {
            helpLink = theValue;
            return *this;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetHelpLink(const std::string& theValue)
        {
            helpLink = theValue;
            return *this;
        }

        HWIN_EXPORT HRESULT ExceptionData::GetHRESULT() const
        {
            return hResult;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetHRESULT(HRESULT theValue)
        {
            hResult = theValue;
            return *this;
        }

        HWIN_EXPORT long long ExceptionData::GetCode() const
        {
            return code;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetCode(long long theValue)
        {
            code = theValue;
            return *this;
        }


        HWIN_EXPORT const ExceptionData* ExceptionData::GetInnerException() const
        {
            return innerException;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetInnerException(const ExceptionData* theValue)
        {
            if(innerException != theValue)
            {
                if(innerException)
                {
                    delete innerException;
                }
                innerException = nullptr;
                if(theValue)
                {
                    innerException = theValue->Clone();
                }
            }
            return *this;
        }

        HWIN_EXPORT const std::string& ExceptionData::GetSource() const
        {
            return source;
        }
        HWIN_EXPORT std::string ExceptionData::GetSource()
        {
            return source;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetSource(const char* theValue)
        {
            source = theValue;
            return *this;
        }
        HWIN_EXPORT ExceptionData& ExceptionData::SetSource(const std::string& theValue)
        {
            source = theValue;
            return *this;
        }

        // ----------------------------------------------------------------------
        // Exception
        // ----------------------------------------------------------------------
        HWIN_EXPORT std::string Exception::empty;
        HWIN_EXPORT const ExceptionData* Exception::GetExceptionData() const
        {
            return data;
        }
        HWIN_EXPORT ExceptionData* Exception::GetExceptionData()
        {
            if(!data)
            {
                data = new ExceptionData();
            }
            return data;
        }
        
        __declspec(thread) char ExceptionFormatBuffer[1024];
        HWIN_EXPORT std::string Exception::Format(const char*fmt, ... )
        {
            va_list args;
            va_start(args, fmt);
            
            std::string::size_type length = vsnprintf_s (ExceptionFormatBuffer,1023, fmt, args );
            ExceptionFormatBuffer[1023] = '\x0';
            std::string result(ExceptionFormatBuffer,length);
            va_end(args);
            return result;
        }

        HWIN_EXPORT std::string Exception::FormatList(const char*fmt, va_list args )
        {
            std::string::size_type length = vsnprintf_s (ExceptionFormatBuffer,1023, fmt, args );
            ExceptionFormatBuffer[1023] = '\x0';
            std::string result(ExceptionFormatBuffer,length);
            return result;
        }

        
        HWIN_EXPORT Exception::Exception()
            : data(nullptr)
        {

        }
        
        HWIN_EXPORT Exception::Exception(const Exception& theException)
            : data(nullptr)
        {
            if(theException.data)
            {
                data = theException.data->Clone();
            }
        }
        
        HWIN_EXPORT Exception::Exception(Exception&& theException)
            : data(nullptr)
        {
            if(theException.data)
            {
                data = theException.data;
                theException.data = nullptr;
            }
        }
        
        HWIN_EXPORT Exception::Exception(const char* theMessage)
            : data(nullptr)
        {
            if(theMessage)
            {
                data = new ExceptionData(theMessage);
            }
        }
        
        HWIN_EXPORT Exception::Exception(const std::string& theMessage)
            : data(nullptr)
        {
            data = new ExceptionData(theMessage);
        }
        
        HWIN_EXPORT Exception::Exception(const char* theMessage, const Exception& theInnerException)
        {
            data = new ExceptionData(theMessage,theInnerException.data);
        }
        
        HWIN_EXPORT Exception::~Exception()
        {
            if(data)
            {
                delete data;
            }
        }
        

        HWIN_EXPORT Exception& Exception::operator = (const Exception& theException)
        {
            if(data != theException.data)
            {
                if(data)
                {
                    delete data;
                    data = nullptr;
                }
                if(theException.data)
                {
                    data = theException.data->Clone();
                }
            }
            return *this;
        }
        
        HWIN_EXPORT Exception& Exception::operator = (Exception&& theException)
        {
            if(this != &theException)
            {
                if(data)
                {
                    delete data;
                    data = nullptr;
                }
                if(theException.data)
                {
                    data = theException.data;
                    theException.data = nullptr;
                }
            }
            return *this;
        }
        

        HWIN_EXPORT const char* Exception::what() const
        {
            return GetMessage().c_str();
        }


        HWIN_EXPORT const std::string& Exception::GetMessage() const
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetMessage();
            }
            return empty;
        }
        
        HWIN_EXPORT std::string Exception::GetMessage()
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetMessage();
            }
            return empty;
        }
        
        HWIN_EXPORT Exception& Exception::SetMessage(const char* theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetMessage(theValue);
            return *this;
        }
        
        HWIN_EXPORT Exception& Exception::SetMessage(const std::string& theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetMessage(theValue);
            return *this;
        }
        

        HWIN_EXPORT const std::string& Exception::GetHelpLink() const
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetHelpLink();
            }
            return empty;
        }
        
        HWIN_EXPORT std::string Exception::GetHelpLink()
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetHelpLink();
            }
            return empty;
        }
        
        HWIN_EXPORT Exception& Exception::SetHelpLink(const char* theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetHelpLink(theValue);
            return *this;
        }
        
        HWIN_EXPORT Exception& Exception::SetHelpLink(const std::string& theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetHelpLink(theValue);
            return *this;
        }
        

        HWIN_EXPORT HRESULT Exception::GetHRESULT() const
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetHRESULT();
            }
            return 0;
        }
        
        HWIN_EXPORT Exception& Exception::SetHRESULT(HRESULT theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetHRESULT(theValue);
            return *this;
        }
        

        HWIN_EXPORT long long Exception::GetCode() const
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetCode();
            }
            return 0;
        }
        
        HWIN_EXPORT Exception& Exception::SetCode(long long theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetCode(theValue);
            return *this;
        }
        

        HWIN_EXPORT const ExceptionData* Exception::GetInnerException() const
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetInnerException();
            }
            return nullptr;
        }
        
        HWIN_EXPORT Exception& Exception::SetInnerException(const Exception& theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetInnerException(theValue.data);
            return *this;
        }
        

        HWIN_EXPORT const std::string& Exception::GetSource() const
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetSource();
            }
            return empty;
        }
        
        HWIN_EXPORT std::string Exception::GetSource()
        {
            auto exceptionData = GetExceptionData();
            if(exceptionData)
            {
                return exceptionData->GetSource();
            }
            return empty;
        }
        
        HWIN_EXPORT Exception& Exception::SetSource(const char* theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetSource(theValue);
            return *this;
        }
        
        HWIN_EXPORT Exception& Exception::SetSource(const std::string& theValue)
        {
            auto exceptionData = GetExceptionData();
            exceptionData->SetSource(theValue);
            return *this;
        }
        
        // ----------------------------------------------------------------------
        //
        // ----------------------------------------------------------------------
        HWIN_EXPORT ArgumentException::ArgumentException(const char* message, const char* argument)
        {
            std::string msg = Format("%s: %s",argument,message);
            SetMessage(msg);
        }
        HWIN_EXPORT ArgumentException::ArgumentException(const std::string& message, const char* argument )
        {
            std::string msg = Format("%s: %s",argument,message.c_str());
            SetMessage(msg);
        }
        HWIN_EXPORT ArgumentException::ArgumentException(const std::string& message, const std::string& argument)
        {
            std::string msg = Format("%s: %s",argument.c_str(),message.c_str());
            SetMessage(msg);
        }

        // ----------------------------------------------------------------------
        //
        // ----------------------------------------------------------------------
        HWIN_EXPORT ArgumentOutOfRangeException::ArgumentOutOfRangeException(const char* argument, const char* message)
        {
            std::string msg = Format("%s: %s",argument,message);
            SetMessage(msg);
        }
        HWIN_EXPORT ArgumentOutOfRangeException::ArgumentOutOfRangeException(const char* argument, const std::string& message)
        {
            std::string msg = Format("%s: %s",argument,message.c_str());
            SetMessage(msg);
        }
        HWIN_EXPORT ArgumentOutOfRangeException::ArgumentOutOfRangeException(const std::string& argument, const std::string& message)
        {
            std::string msg = Format("%s: %s",argument.c_str(),message.c_str());
            SetMessage(msg);
        }



        // ----------------------------------------------------------------------
        //
        // ----------------------------------------------------------------------
        __declspec(thread) char buffer[512] = {0,};
        HWIN_EXPORT void CheckHRESULT(HRESULT hresult)
        {
            if(FAILED(hresult))
            {
                int length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ARGUMENT_ARRAY, nullptr, hresult, 0, buffer, 512, nullptr);
                if(length)
                {
                    throw std::exception(buffer);
                }
                else
                {
                    throw std::exception("Unknown error");
                }
            }
        }

        HWIN_EXPORT void ThrowLastOSError()
        {
            DWORD errorId = ::GetLastError();
            ThrowOSError(errorId);
        }

        

        HWIN_EXPORT void ThrowOSError(DWORD errorId)
        {
            int length = 0;
            if(errorId)
            {
                length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ARGUMENT_ARRAY, nullptr, errorId, 0, buffer, 512, nullptr);
                if(length)
                {
                    char* ptr = buffer;
                    throw std::exception(ptr);
                }
                else
                {
                    throw std::exception("Unknown error");
                }
            }
        }


        HWIN_EXPORT void ThrowNoInterface()
        {
            throw std::exception("No interface");
        }

        HWIN_EXPORT void ThrowPointerIsNULL()
        {
            throw std::exception("pointer is null");
        }

        HWIN_EXPORT void ThrowInvalidHandle()
        {
            throw std::exception("invalid handle");
        }


        HWIN_EXPORT HRESULT HRESULTFromException(const std::exception& exception)
        {
            const std::exception* ptr = &exception;
            const Exception* pException = dynamic_cast<const Exception*>(ptr);
            if(pException)
            {
                return pException->GetHRESULT();
            }
            else
            {
                return E_FAIL;
            }
        }


    };
};
